function childrenText(token) {
  let text = ""
  for (let i = 0; i < token.children.length; i++) {
    let child = token.children[i];
    if (child.type == "text" || child.type == "code_inlin") text += child.content
  }
  return text
}

function hash(text) {
  let sum = require("crypto").createHash("sha1")
  sum.update(text)
  return sum.digest("base64").slice(0, 10)
}

function startAndEnd(text) {
  var words = text.split(/\W+/);
  if (!words[0]) words.shift();
  if (!words[words.length - 1]) words.pop();
  if (words.length <= 6) return words.join(" ");
  return words.slice(0, 3).concat(words.slice(words.length - 3)).join(" ");
}

function tokenText(token) {
  if (token.type == "text" || token.type == "code_inline") return token.content
  else if (token.type == "softbreak") return " "
  else return ""
}

function smartQuotes(tokens, i, tex, moveQuotes) {
  let text = tokens[i].content, from = 0
  for (let j = i - 1, tt; j >= 0; j--) if (tt = tokenText(tokens[j])) {
    text = tt + text
    from = tt.length
    break
  }
  let to = text.length
  for (let j = i + 1, tt; j < tokens.length; j++) if (tt = tokenText(tokens[j])) {
    text += tt
    break
  }

  let quoted = text
    .replace(/([\w\.,!?\)])'/g, "$1’")
    .replace(/'(\w|\(\()/g, "‘$1")
    .replace(/([\w\.,!?\)])"/g, "$1”")
    .replace(/"(\w|\(\()/g, "“$1")
    .slice(from, to)
  if (moveQuotes) quoted = quoted.replace(/”([.!?:;,]+)/g, "$1”")
  return tex ? quoted.replace(/‘/g, "`").replace(/’/g, "'").replace(/“/g, "``").replace(/”/g, "''") : quoted
}

function handleIf(tokens, i, options) {
  let tag = tokens[i].args[0]
  if (options.defined.indexOf(tag) > -1) return i
  for (let j = i + 1; j < tokens.length; j++) if (tokens[j].type == "meta_if_close" && tokens[j].args[0] == tag)
    return j
}

const titleCaseSmallWords = "a an the at by for in of on to up and as but with or nor if console.log".split(" ");

function capitalizeTitle(text) {
  return text.split(" ")
    .map(word => titleCaseSmallWords.includes(word) ? word : word[0].toUpperCase() + word.slice(1))
    .join(" ")
}

function transformInline(tokens, options, prevType) {
  let capitalize = options.capitalizeTitles && prevType == "heading_open"
  let result = []
  for (let i = 0; i < tokens.length; i++) {
    let tok = tokens[i], type = tok.type
    if (type == "meta_if_close" || (options.index === false && type == "meta_index")) {
      // Drop
    } else if (type == "meta_if_open") {
      i = handleIf(tokens, i, options)
    } else {
      if (type == "text" && /[\'\"]/.test(tok.content)) tok.content = smartQuotes(tokens, i, options.texQuotes, options.moveQuotes)
      if (capitalize) tok.content = capitalizeTitle(tok.content)
      result.push(tok)
    }
  }
  return result
}

function nextTag(tokens, i) {
  for (let j = i + 1; j < tokens.length; j++) if (tokens[j].tag) return tokens[j];
}

exports.transformTokens = function(tokens, options) {
  let meta = {}, result = []
  for (let i = 0; i < tokens.length; i++) {
    let tok = tokens[i], type = tok.type
    if (type == "meta_meta") {
      for (let prop in tok.args[0]) meta[prop] = tok.args[0][prop]
    } else if (type == "meta_id") {
      let next = nextTag(tokens, i)
      ;(next.attrs || (next.attrs = [])).push(["id", tok.args[0]])
    } else if (type == "meta_table") {
      nextTag(tokens, i).tableData = tok.args[0]
    } else if (type == "meta_if_open") {
      i = handleIf(tokens, i, options)
    } else if (type == "meta_hint_open" && options.strip === "hints") {
      do { i++ } while (tokens[i].type != "meta_hint_close")
    } else if (type == "meta_if_close" || (options.index === false && (type == "meta_indexsee" || type == "meta_index"))) {
      // Drop
    } else if (tok.tag == "h1" && options.takeTitle) {
      if (tokens[i + 1].children.length != 1) throw new Error("Complex H1 not supported")
      meta.title = tokens[i + 1].children[0].content
      i += 2
    } else {
      if (type == "paragraph_open")
        tok.hashID = "p_" + hash(startAndEnd(childrenText(tokens[i + 1])))
      else if (type == "heading_open")
        tok.hashID = (tok.tag == "h2" ? "h_" : "i_") + hash(childrenText(tokens[i + 1]))
      else if (type == "fence")
        tok.hashID = "c_" + hash(tok.content)

      if (tok.children) tok.children = transformInline(tok.children, options, tokens[i - 1].type)

      result.push(tok)
    }
  }
  return {tokens: result, metadata: meta}
}
